<html>
    <head>
        <title>三维变换学习</title>
        <link rel="stylesheet" type="text/css" href="lib/webgl-tutorials.css"/>
    </head>
    <body onload="main()">
        <canvas id = "c"></canvas>
        <div id="uiContainer">
            <div id="ui">
                <div id="fieldOfView"></div>
                <div id="x"></div>
                <div id="y"></div>
                <div id="z"></div>
                <div id="angleX"></div>
                <div id="angleY"></div>
                <div id="angleZ"></div>
                <div id="scaleX"></div>
                <div id="scaleY"></div>
                <div id="scaleZ"></div>
                <div id="scaleXYZ"></div>
            </div>
            </div>
    </body>
    <script type='text/javascript' src="lib/webgl-utils.js"></script>
    <script type='text/javascript' src="lib/webgl-lessons-ui.js"></script>
    <script id="vertex-shader-2d" type="x-shaderx-shader/x-vertex">
        attribute vec4 a_Position;
        uniform mat4 u_Transform;
        attribute vec4 a_Color;
        varying vec4 v_Color;
        void main(){
            gl_Position = u_Transform * a_Position;
            v_Color = a_Color;
        }
    </script>
    <script id="fragment-shader-2d" type="x-shader/x-fragment">
        precision mediump float;
        //uniform vec4 u_FragColor;
        varying vec4 v_Color;
        void main(){
            gl_FragColor = v_Color;
        }
    </script>
    <script type="text/javascript">
        var m4 = {
                //坐标转换形成矩阵 
                // ( x / 400) * 2 - 1
                // projection: function(width, height, depth){
                //     return [
                //         2 / width, 0, 0, 0,
                //         0, -2 / height, 0, 0, 
                //         0, 0, 2 / depth, 0,
                //         -1, 1, 0, 1
                //     ]
                // },
                //正交
                orthographic: function(left, right, bottom, top, near, far) {
                    return [
                        2 / (right - left), 0, 0, 0,
                        0, 2 / (top - bottom), 0, 0,
                        0, 0, 2 / (near - far), 0,
                    
                        (left + right) / (left - right),
                        (bottom + top) / (bottom - top),
                        (near + far) / (near - far),
                        1,
                    ];
                },
                //透视
                perspective: function(fieldOfViewInRadians, aspect, near, far) {
                    var f = Math.tan(Math.PI * 0.5 - 0.5 * fieldOfViewInRadians);
                    var rangeInv = 1.0 / (near - far);
                
                    return [
                        f / aspect, 0, 0, 0,
                        0, f, 0, 0,
                        0, 0, (near + far) * rangeInv, -1,
                        0, 0, near * far * rangeInv * 2, 0
                    ];
                },
                identity: function(){
                    return [
                        1, 0, 0, 0,
                        0, 1, 0, 0,
                        0, 0, 1, 0, 
                        0, 0, 0, 1
                    ]
                },
                translation: function (tx, ty, tz){
                    return [
                        1, 0, 0, 0,
                        0, 1, 0, 0,
                        0, 0, 1, 0,
                        tx, ty, tz, 1
                    ]
                },
                translate: function (m, tx, ty, tz){
                    return m4.multiply(m, m4.translation(tx, ty, tz));
                },
                xRotation: function (radiansAngle){
                    var c = Math.cos(radiansAngle);
                    var s = Math.sin(radiansAngle);
                    return [
                            1, 0, 0, 0,
                            0, c, s, 0,
                            0, -s, c, 0,
                            0, 0, 0, 1,
                        ]
                },
                xRotate: function (m, radiansAngle){
                    return m4.multiply(m, m4.xRotation(radiansAngle));
                },
                yRotation: function (radiansAngle){
                    var c = Math.cos(radiansAngle);
                    var s = Math.sin(radiansAngle);
                    return [
                            c, 0, -s, 0,
                            0, 1, 0, 0,
                            s, 0, c, 0,
                            0, 0, 0, 1,
                        ]
                },
                yRotate: function (m, radiansAngle){
                    return m4.multiply(m, m4.yRotation(radiansAngle));
                },
                zRotation: function (radiansAngle){
                    var c = Math.cos(radiansAngle);
                    var s = Math.sin(radiansAngle);
                    return[
                            c, s, 0, 0,
                            -s, c, 0, 0,
                            0, 0, 1, 0,
                            0, 0, 0, 1,
                        ];
                },
                zRotate: function (m, radiansAngle){
                    return m4.multiply(m, m4.zRotation(radiansAngle));
                },
                scaling: function(sx, sy, sz){
                    return[
                        sx, 0, 0, 0,
                        0, sy, 0, 0,
                        0, 0, sz, 0,
                        0, 0, 0, 1
                    ]
                },
                scale: function (m, sx, sy, sz){
                    return m4.multiply(m, m4.scaling(sx, sy, sz));
                },
                degToRad: function(d){
                    return d * Math.PI / 180;
                },
                radToDeg: function (r) {
                    return r * 180 / Math.PI;
                },
                multiply: function(a, b) {
                    var a00 = a[0 * 4 + 0];
                    var a01 = a[0 * 4 + 1];
                    var a02 = a[0 * 4 + 2];
                    var a03 = a[0 * 4 + 3];
                    var a10 = a[1 * 4 + 0];
                    var a11 = a[1 * 4 + 1];
                    var a12 = a[1 * 4 + 2];
                    var a13 = a[1 * 4 + 3];
                    var a20 = a[2 * 4 + 0];
                    var a21 = a[2 * 4 + 1];
                    var a22 = a[2 * 4 + 2];
                    var a23 = a[2 * 4 + 3];
                    var a30 = a[3 * 4 + 0];
                    var a31 = a[3 * 4 + 1];
                    var a32 = a[3 * 4 + 2];
                    var a33 = a[3 * 4 + 3];
                    var b00 = b[0 * 4 + 0];
                    var b01 = b[0 * 4 + 1];
                    var b02 = b[0 * 4 + 2];
                    var b03 = b[0 * 4 + 3];
                    var b10 = b[1 * 4 + 0];
                    var b11 = b[1 * 4 + 1];
                    var b12 = b[1 * 4 + 2];
                    var b13 = b[1 * 4 + 3];
                    var b20 = b[2 * 4 + 0];
                    var b21 = b[2 * 4 + 1];
                    var b22 = b[2 * 4 + 2];
                    var b23 = b[2 * 4 + 3];
                    var b30 = b[3 * 4 + 0];
                    var b31 = b[3 * 4 + 1];
                    var b32 = b[3 * 4 + 2];
                    var b33 = b[3 * 4 + 3];
                    return [
                        b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30,
                        b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31,
                        b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32,
                        b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33,
                        b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30,
                        b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31,
                        b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32,
                        b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33,
                        b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30,
                        b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31,
                        b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32,
                        b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33,
                        b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30,
                        b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31,
                        b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32,
                        b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33,
                    ];
                }
            }

        var translation = [0, 0, -350];//左上位置
        var angleInRadians = [m4.degToRad(190), m4.degToRad(40), m4.degToRad(320)];//角度
        var scale = [1, 1, 1];//缩放因子
        var color = [Math.random(), Math.random(), Math.random(), 1];
        var fieldOfView = m4.degToRad(60);

        function main(){
            var canvas = document.getElementById("c");
            var gl = canvas.getContext("webgl");
            if(!gl){
                error("not supported webgl.");
                return; 
            }

            setupUI();

        
            //编译shader
            var program = webglUtils.createProgramFromScripts(gl, ["vertex-shader-2d", "fragment-shader-2d"]);
            var a_Position = gl.getAttribLocation(program, "a_Position");
            var u_Transform = gl.getUniformLocation(program, "u_Transform");
            //var u_FragColor = gl.getUniformLocation(program, "u_FragColor");
            var a_Color = gl.getAttribLocation(program, "a_Color");

            //顶点buffer
            var vertexBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
            //设置顶点属性
            setRectangle();

            var colorBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
            setColors();

            //drawScene
            drawScene();

            //requestAnimationFrame(animation);

            //测试动画
            function animation(){
                angleInRadians[0] -= 0.01;
                translation[0] += 0.4;
                translation[1] += 0.4;
                //scale[0] -= 0.001;
                //scale[1] -= 0.001;
                drawScene();
                requestAnimationFrame(animation);
            }

            //绘制
            function drawScene(){
                webglUtils.resizeCanvasToDisplaySize(gl.canvas);
                gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

                gl.clearColor(0.0, 0.0, 0.0, 1.0);
                gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

                gl.enable(gl.CULL_FACE);//剔除成遮挡面
                gl.enable(gl.DEPTH_TEST);//深度缓冲区

                gl.useProgram(program);

                gl.enableVertexAttribArray(a_Position);
                gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
                gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false,  0, 0);

                //gl.uniform4fv(u_FragColor, color);
                gl.enableVertexAttribArray(a_Color);
                gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
                gl.vertexAttribPointer(a_Color, 3, gl.UNSIGNED_BYTE, true,  0, 0); //normlize => 0至255标准化为 0-1

                //计算矩阵
                //var projectionMatrix = m4.projection(gl.canvas.width, gl.canvas.height, 400);
                //var projectionMatrix = m4.orthographic(0, gl.canvas.width, gl.canvas.height, 0, 400, -400);//left right bottom top near far
                console.log(fieldOfView);
                var projectionMatrix = m4.perspective(fieldOfView, gl.canvas.clientWidth / gl.canvas.clientHeight, 1, 2000);
                var matrix = m4.translate(projectionMatrix, translation[0], translation[1], translation[2]);
                matrix = m4.xRotate(matrix, angleInRadians[0]);
                matrix = m4.yRotate(matrix, angleInRadians[1]);
                matrix = m4.zRotate(matrix, angleInRadians[2]);
                matrix = m4.scale(matrix, scale[0], scale[1], scale[2]);

                gl.uniformMatrix4fv(u_Transform, false, matrix);

                gl.drawArrays(gl.TRIANGLES, 0, 6 * 16);
            }

            //顶点属性
            function setRectangle(){
                gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
                    // left column front
                    0,   0,  0,
                    0, 150,  0,
                    30,   0,  0,
                    0, 150,  0,
                    30, 150,  0,
                    30,   0,  0,

                    // top rung front
                    30,   0,  0,
                    30,  30,  0,
                    100,   0,  0,
                    30,  30,  0,
                    100,  30,  0,
                    100,   0,  0,

                    // middle rung front
                    30,  60,  0,
                    30,  90,  0,
                    67,  60,  0,
                    30,  90,  0,
                    67,  90,  0,
                    67,  60,  0,

                    // left column back
                        0,   0,  30,
                    30,   0,  30,
                        0, 150,  30,
                        0, 150,  30,
                    30,   0,  30,
                    30, 150,  30,

                    // top rung back
                    30,   0,  30,
                    100,   0,  30,
                    30,  30,  30,
                    30,  30,  30,
                    100,   0,  30,
                    100,  30,  30,

                    // middle rung back
                    30,  60,  30,
                    67,  60,  30,
                    30,  90,  30,
                    30,  90,  30,
                    67,  60,  30,
                    67,  90,  30,

                    // top
                        0,   0,   0,
                    100,   0,   0,
                    100,   0,  30,
                        0,   0,   0,
                    100,   0,  30,
                        0,   0,  30,

                    // top rung right
                    100,   0,   0,
                    100,  30,   0,
                    100,  30,  30,
                    100,   0,   0,
                    100,  30,  30,
                    100,   0,  30,

                    // under top rung
                    30,   30,   0,
                    30,   30,  30,
                    100,  30,  30,
                    30,   30,   0,
                    100,  30,  30,
                    100,  30,   0,

                    // between top rung and middle
                    30,   30,   0,
                    30,   60,  30,
                    30,   30,  30,
                    30,   30,   0,
                    30,   60,   0,
                    30,   60,  30,

                    // top of middle rung
                    30,   60,   0,
                    67,   60,  30,
                    30,   60,  30,
                    30,   60,   0,
                    67,   60,   0,
                    67,   60,  30,

                    // right of middle rung
                    67,   60,   0,
                    67,   90,  30,
                    67,   60,  30,
                    67,   60,   0,
                    67,   90,   0,
                    67,   90,  30,

                    // bottom of middle rung.
                    30,   90,   0,
                    30,   90,  30,
                    67,   90,  30,
                    30,   90,   0,
                    67,   90,  30,
                    67,   90,   0,

                    // right of bottom
                    30,   90,   0,
                    30,  150,  30,
                    30,   90,  30,
                    30,   90,   0,
                    30,  150,   0,
                    30,  150,  30,

                    // bottom
                    0,   150,   0,
                    0,   150,  30,
                    30,  150,  30,
                    0,   150,   0,
                    30,  150,  30,
                    30,  150,   0,

                    // left side
                    0,   0,   0,
                    0,   0,  30,
                    0, 150,  30,
                    0,   0,   0,
                    0, 150,  30,
                    0, 150,   0
                ]), gl.STATIC_DRAW);
            }

            function setColors(){
                gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array([
                        // left column front
                        200,  70, 120,
                        200,  70, 120,
                        200,  70, 120,
                        200,  70, 120,
                        200,  70, 120,
                        200,  70, 120,

                        // top rung front
                        200,  70, 120,
                        200,  70, 120,
                        200,  70, 120,
                        200,  70, 120,
                        200,  70, 120,
                        200,  70, 120,

                        // middle rung front
                        200,  70, 120,
                        200,  70, 120,
                        200,  70, 120,
                        200,  70, 120,
                        200,  70, 120,
                        200,  70, 120,

                        // left column back
                        80, 70, 200,
                        80, 70, 200,
                        80, 70, 200,
                        80, 70, 200,
                        80, 70, 200,
                        80, 70, 200,

                        // top rung back
                        80, 70, 200,
                        80, 70, 200,
                        80, 70, 200,
                        80, 70, 200,
                        80, 70, 200,
                        80, 70, 200,

                        // middle rung back
                        80, 70, 200,
                        80, 70, 200,
                        80, 70, 200,
                        80, 70, 200,
                        80, 70, 200,
                        80, 70, 200,

                        // top
                        70, 200, 210,
                        70, 200, 210,
                        70, 200, 210,
                        70, 200, 210,
                        70, 200, 210,
                        70, 200, 210,

                        // top rung right
                        200, 200, 70,
                        200, 200, 70,
                        200, 200, 70,
                        200, 200, 70,
                        200, 200, 70,
                        200, 200, 70,

                        // under top rung
                        210, 100, 70,
                        210, 100, 70,
                        210, 100, 70,
                        210, 100, 70,
                        210, 100, 70,
                        210, 100, 70,

                        // between top rung and middle
                        210, 160, 70,
                        210, 160, 70,
                        210, 160, 70,
                        210, 160, 70,
                        210, 160, 70,
                        210, 160, 70,

                        // top of middle rung
                        70, 180, 210,
                        70, 180, 210,
                        70, 180, 210,
                        70, 180, 210,
                        70, 180, 210,
                        70, 180, 210,

                        // right of middle rung
                        100, 70, 210,
                        100, 70, 210,
                        100, 70, 210,
                        100, 70, 210,
                        100, 70, 210,
                        100, 70, 210,

                        // bottom of middle rung.
                        76, 210, 100,
                        76, 210, 100,
                        76, 210, 100,
                        76, 210, 100,
                        76, 210, 100,
                        76, 210, 100,

                        // right of bottom
                        140, 210, 80,
                        140, 210, 80,
                        140, 210, 80,
                        140, 210, 80,
                        140, 210, 80,
                        140, 210, 80,

                        // bottom
                        90, 130, 110,
                        90, 130, 110,
                        90, 130, 110,
                        90, 130, 110,
                        90, 130, 110,
                        90, 130, 110,

                        // left side
                        160, 160, 220,
                        160, 160, 220,
                        160, 160, 220,
                        160, 160, 220,
                        160, 160, 220,
                        160, 160, 220
                ]), gl.STATIC_DRAW)
            }

            function setupUI(){
                webglUtils.resizeCanvasToDisplaySize(gl.canvas);
                webglLessonsUI.setupSlider("#fieldOfView", {value: m4.radToDeg(fieldOfView), slide: updateFieldOfView, max: 300 });
                webglLessonsUI.setupSlider("#x", {value: translation[0], slide: updatePosition(0), max: gl.canvas.width });
                webglLessonsUI.setupSlider("#y", {value: translation[1], slide: updatePosition(1), max: gl.canvas.height});
                webglLessonsUI.setupSlider("#z", {value: translation[2], slide: updatePosition(2), min: -800, max: 100});
                webglLessonsUI.setupSlider("#angleX", {value: m4.radToDeg(angleInRadians[0]), slide: updateAngle(0), max: 360});
                webglLessonsUI.setupSlider("#angleY", {value: m4.radToDeg(angleInRadians[1]), slide: updateAngle(1), max: 360});
                webglLessonsUI.setupSlider("#angleZ", {value: m4.radToDeg(angleInRadians[2]), slide: updateAngle(2), max: 360});
                webglLessonsUI.setupSlider("#scaleX", {value: scale[0], slide: updateScale(0), min: -5, max: 5, step: 0.01, precision: 2 });
                webglLessonsUI.setupSlider("#scaleY", {value: scale[1], slide: updateScale(1), min: -5, max: 5, step: 0.01, precision: 2 });
                webglLessonsUI.setupSlider("#scaleZ", {value: scale[2], slide: updateScale(2), min: -5, max: 5, step: 0.01, precision: 2 });
                webglLessonsUI.setupSlider("#scaleXYZ", {value: scale[0], slide: updateScaleXYZ, min: -5, max: 5, step: 0.01, precision: 2 });
            }

            function updatePosition(index){
                return function(event, ui) {
                    translation[index] = ui.value;
                    drawScene();
                };
            }

            function updateAngle(index) {
                return function(event, ui) {
                    angleInRadians[index] = m4.degToRad(ui.value);
                    drawScene();
                };
            }

            function updateScale(index){
                return function(event, ui) {
                    scale[index] = ui.value;
                    drawScene();
                };
            }

            function updateScaleXYZ(event, ui){
                scale[0] = ui.value;
                scale[1] = ui.value;
                scale[2] = ui.value;
                drawScene();
            }

            function updateFieldOfView(event, ui){
                fieldOfView = m4.degToRad(ui.value);
                drawScene();
            }
        }

        
    </script>
</html>